﻿﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ThePresenter.Interop;
using Draw = System.Drawing;
using WinForms = System.Windows.Forms;


namespace ThePresenter.Windows
{
	public static class Helpers
	{
		#region Size.Resize
		/// <summary>
		/// Resizes the specified size and preserve aspect ratio.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="target">The target size.</param>
		/// <returns>New size.</returns>
		public static Size Resize(this Size source, Size target)
		{
			Size newSize;
			double ratio;

			{
				double x_ratio, y_ratio;

				x_ratio = source.Width / target.Width;
				y_ratio = source.Height / target.Height;

				if (x_ratio > y_ratio)
				{
					newSize = new Size(source.Width / x_ratio, source.Height / x_ratio);
					ratio = 96 / x_ratio;
				}
				else
				{
					newSize = new Size(source.Width / y_ratio, source.Height / y_ratio);
					ratio = 96 / y_ratio;
				}
			}

			return newSize;
		}
		#endregion


		#region GetThumbnailAsStream
		/// <summary>
		/// Gets the thumbnail as stream.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="targetSize">Size of the target.</param>
		/// <returns>The thumbnail as stream.</returns>
		public static MemoryStream GetThumbnailAsStream(this FrameworkElement source, Size targetSize)
		{
			Size actualSize;

			if (source.DesiredSize != default(Size) && !double.IsNaN(source.DesiredSize.Width) && !double.IsNaN(source.DesiredSize.Height))
				actualSize = source.DesiredSize;
			else if (!double.IsNaN(source.Width) && !double.IsNaN(source.Height))
				actualSize = new Size(source.Width, source.Height);
			else
				throw new ArgumentException("Unable to determine source's Size.", "source");

			return GetThumbnailAsStream(source, actualSize, targetSize);
		}

		/// <summary>
		/// Gets the thumbnail as stream.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="actualSize">The actual size.</param>
		/// <param name="targetSize">Size of the target.</param>
		/// <returns>The thumbnail as stream.</returns>
		public static MemoryStream GetThumbnailAsStream(this Visual source, Size actualSize, Size targetSize)
		{
			Size newSize;
			double ratio;

			{
				double x_ratio = actualSize.Width / targetSize.Width;
				double y_ratio = actualSize.Height / targetSize.Height;

				if (x_ratio > y_ratio)
				{
					newSize = new Size(actualSize.Width / x_ratio, actualSize.Height / x_ratio);
					ratio = 96 / x_ratio;
				}
				else
				{
					newSize = new Size(actualSize.Width / y_ratio, actualSize.Height / y_ratio);
					ratio = 96 / y_ratio;
				}
			}

			RenderTargetBitmap bitmap = new RenderTargetBitmap((int)newSize.Width, (int)newSize.Height, ratio, ratio, PixelFormats.Pbgra32);

			bitmap.Render(source);

			PngBitmapEncoder image = new PngBitmapEncoder();
			image.Frames.Add(BitmapFrame.Create(bitmap));

			var ms = new MemoryStream();
			image.Save(ms);
			ms.Position = 0;

			return ms;
		}
		#endregion


		#region GetThumbnail
		/// <summary>
		/// Gets the thumbnail.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="targetSize">Size of the target.</param>
		/// <returns>The thumbnail.</returns>
		public static ImageSource GetThumbnail(this FrameworkElement source, Size targetSize)
		{
			return Helpers.GetBitmapImageFromStream(GetThumbnailAsStream(source, targetSize));
		}

		/// <summary>
		/// Gets the thumbnail.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="actualSize">The actual size.</param>
		/// <param name="targetSize">Size of the target.</param>
		/// <returns>The the thumbnail.</returns>
		public static ImageSource GetThumbnail(this Visual source, Size actualSize, Size targetSize)
		{
			return Helpers.GetBitmapImageFromStream(GetThumbnailAsStream(source, actualSize, targetSize));
		}
		#endregion


		#region GetColorFromHexString
		/// <summary>
		/// Gets the color from hex string.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The color from hex string.</returns>
		public static Color GetColorFromHexString(string value)
		{
			if (value.StartsWith("#")) // hex string - solid color
			{
				byte r, g, b, a;

				a = (byte.Parse(value.Substring(1, 2), NumberStyles.HexNumber));
				r = (byte.Parse(value.Substring(3, 2), NumberStyles.HexNumber));
				g = (byte.Parse(value.Substring(5, 2), NumberStyles.HexNumber));
				b = (byte.Parse(value.Substring(7, 2), NumberStyles.HexNumber));

				return Color.FromArgb(a, r, g, b);
			}

			return Colors.Transparent;
		}
		#endregion


		#region GetScreen
		/// <summary>
		/// Gets the screen from the window.
		/// </summary>
		/// <param name="window">The window.</param>
		/// <returns>Screen.</returns>
		public static WinForms.Screen GetScreen(this Window window)
		{
			return WinForms.Screen.FromHandle(new WindowInteropHelper(window).Handle);
		}
		#endregion


		/// <summary>
		/// Converts <see cref="System.Drawing.Bitmap"/> to <see cref="System.Windows.Media.Imaging.BitmapSource"/>.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <returns>New instance of <see cref="System.Windows.Media.Imaging.BitmapSource"/>.</returns>
		/// <exception cref="System.OutOfMemoryException"/>
		static BitmapSource GetBitmapSource(Draw.Bitmap source)
		{
			IntPtr hBitmap = source.GetHbitmap();

			try
			{
				return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
			}
			catch (Win32Exception)
			{
				return null;
			}
			finally
			{
				NativeMethods.DeleteObject(hBitmap);
			}
		}

		/// <summary>
		/// Takes the screenshot.
		/// </summary>
		/// <param name="rect">The rectangle area of screen.</param>
		/// <returns>New instance of <see cref="System.Windows.Media.Imaging.BitmapSource"/>.</returns>
		/// <exception cref="System.OutOfMemoryException"/>
		public static BitmapSource TakeScreenshot(Draw.Rectangle rect)
		{
			BitmapSource src = null;

			using (Draw.Bitmap screen = new Draw.Bitmap(rect.Width, rect.Height))
			{
				using (Draw.Graphics g = Draw.Graphics.FromImage(screen))
				{
					g.CopyFromScreen(rect.X, rect.Y, 0, 0, new System.Drawing.Size(rect.Width, rect.Height));
				}
				
				src = GetBitmapSource(screen);
			}

			return src;
		}

		/// <summary>
		/// Takes the screenshot image.
		/// </summary>
		/// <param name="rect">The rectangle area of screen.</param>
		/// <returns>New instance of <see cref="System.Windows.Media.Imaging.BitmapImage"/>.</returns>
		/// <exception cref="System.OutOfMemoryException"/>
		public static BitmapImage TakeScreenshotImage(Draw.Rectangle rect)
		{
			var ms = new MemoryStream(150 * 1024); // do not dispose!
			{
				try
				{
					using (Draw.Image image = new Draw.Bitmap(rect.Width, rect.Height)) // throws ArgumentException when out of memory
					{
						using (Draw.Graphics g = Draw.Graphics.FromImage(image))
						{
							g.CopyFromScreen(rect.X, rect.Y, 0, 0, new System.Drawing.Size(rect.Width, rect.Height));
						}

						// TODO Use TakeScreenshot(rect)
						image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);

						return GetBitmapImageFromStream(ms);
					}
				}
				catch (ArgumentException ex)
				{
					throw new OutOfMemoryException("Too much images taken.", ex);
				}
			}
		}

		/// <summary>
		/// Gets the bitmap image from stream.
		/// </summary>
		/// <param name="stream">The stream.</param>
		/// <returns>New instance of <see cref="System.Windows.Media.Imaging.BitmapImage"/>.</returns>
		public static BitmapImage GetBitmapImageFromStream(Stream stream)
		{
			var bitmap = new BitmapImage();

			bitmap.BeginInit();
			bitmap.StreamSource = stream;
			bitmap.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
			bitmap.EndInit();

			return bitmap;
		}

		///// <summary>
		///// Processes the key input.
		///// </summary>
		///// <param name="key">The key.</param>
		//public static void ProcessInput(Key key)
		//{
		//    KeyEventArgs args = new KeyEventArgs(InputManager.Current.PrimaryKeyboardDevice, PresentationSource.FromVisual(Application.Current.MainWindow), System.Environment.ProcessorCount, key);

		//    args.RoutedEvent = UIElement.KeyDownEvent;
		//    Application.Current.MainWindow.RaiseEvent(args);

		//    args.RoutedEvent = UIElement.KeyUpEvent;
		//    Application.Current.MainWindow.RaiseEvent(args);
		//}
	}
}
